#include "Object/NewObjectInner.h"

#include <cassert>

#include "Reflection/Class.h"
#include "Object/Object.h"

#include "Object/RootObject.h"

static RObject * CreateTransientAsset(RObject* InOwner)
{
    SObjectHead* ObjectHead=static_cast<SObjectHead*>(malloc(sizeof(RObject) + sizeof(SObjectHead) ));    
    new (ObjectHead) SObjectHead();
    ObjectHead->RawName=U"TransientAsset";
    
    RObject* Object= ObjectHead->GetThisObject();
    Object->SelfHead=ObjectHead;
    Object->SetOwner(InOwner);
    new (Object) RObject();  

#ifdef USING_SOURCE_LOCATION
    Object->CreatePosition=std::source_location::current();
#endif

    return Object;
};

static RRootObject* CreateRootObject()
{
    SObjectHead* ObjectHead=static_cast<SObjectHead*>(malloc(sizeof(RRootObject) + sizeof(SObjectHead) ));    
    new (ObjectHead) SObjectHead();
    ObjectHead->RawName=U"Root";
    
    RObject* Object= ObjectHead->GetThisObject();
    Object->SelfHead=ObjectHead;
    Object->SetOwner(nullptr);
    new (Object) RRootObject();

#ifdef USING_SOURCE_LOCATION
    Object->CreatePosition=std::source_location::current();
#endif

    RRootObject* RootObject=(RRootObject*)Object;
    RootObject->TransientAsset=CreateTransientAsset(RootObject);

    return RootObject;
}

RRootObject* WH::GetRootObject()
{
    static RRootObject* RootObject=CreateRootObject();
    return RootObject;
}

RObject* WH::GetTransientAsset()
{
    return WH::GetRootObject()->TransientAsset.Get();
}



RObject* NewObjectFirstStage(CClass* InClass, RObject* InOwner
#ifdef USING_SOURCE_LOCATION
        ,const std::source_location& CreateLocation
#endif  
)
{
    assert(InClass && "Must have a class");

    RObject* Owner= InOwner? InOwner:WH::GetTransientAsset();

    //allocate memory
    SObjectHead* ObjectHead=static_cast<SObjectHead*>(malloc(InClass->GetSize() + sizeof(SObjectHead) ));    
    new (ObjectHead) SObjectHead();
    ObjectHead->RawName=InClass->GetShortName();

    RObject* Object= ObjectHead->GetThisObject();
    //to enable WeakObjectPtr
    //to convinent traversal all objects;
    //CObjectGlobals::Get()->AllObjectArray->AddObject(Object);
    Object->SelfHead=ObjectHead;
    Object->SetOwner(Owner);

    //invoke object constructor here
    InClass->InitInstance(Object);

#ifdef USING_SOURCE_LOCATION
    Object->CreatePosition=CreateLocation;
#endif 

    Object->SelfHead->MarkFirstStageFinished();

    return Object;
}

RObject* NewObjectInner(
        CClass* InClass
        , RObject* InOwner
#ifdef USING_SOURCE_LOCATION
        ,const std::source_location& CreateLocation
#endif  
)
{
    RObject* Object=NewObjectFirstStage(InClass,InOwner
#ifdef USING_SOURCE_LOCATION
        ,CreateLocation
#endif
    );

    Object->FirstTimeInitializeObject();
    Object->GetObjectHead()->MarkSecondStageFinished();

    return Object;
}

void DestroyObjectInner(RObject *InObject)
{
    SObjectHead* Head= InObject->GetObjectHead();

    if(Head->RefCounterPtr)
    {
        Head->RefCounterPtr->RefCount=0;
        Head->RefCounterPtr->ObjectHead =nullptr;
    }

    if(Head->WeakRefCounterPtr)
    {
        Head->WeakRefCounterPtr->ObjectHead =nullptr;
    }
    
    TVector<RObject*> Children=Head->Children;
    for(auto& Child : Children)
    {
        WH::DestroyObject(Child);
    }

    InObject->SetOwner(nullptr);

    InObject->GetClass()->EraseInstance(InObject);
    Head->~SObjectHead();

    free(Head);
}

void WH::DestroyObject(RObject *InObject)
{
    SObjectHead* Head= InObject->GetObjectHead();

    if(Head->IsDestroying())
    {
        return;
    }
    Head->MarkDestroying();

    DestroyObjectInner(InObject);
}

WHENGINE_API void WH::DestroyObjects(const TVector<RObject *> &InObjects)
{
    TVector<RObject *> Objects=InObjects;

    //mark all objects as destroying
    for(auto& Object : Objects)
    {
        Object->GetObjectHead()->MarkDestroying();
    }

    //destroy all objects
    for(auto& Object : Objects)
    {
        DestroyObjectInner(Object);
    }

}